package club.ruanx.auth.filter;

import club.ruanx.bean.ResultVO;
import club.ruanx.security.token.AuthenticationTokenGenerator;
import club.ruanx.util.JsonUtils;
import club.ruanx.util.MimeTypeUtils;
import club.ruanx.util.RequestUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author 阮胜
 * @date 2018/9/14 22:26
 */
@Slf4j
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final String POST = "POST";
    private static final String AUTHENTICATION_METHOD_NOT_SUPPORTS = "不支持的认证方法";
    private static final String USERNAME_OR_PASSWORD_CAN_NOT_BE_NULL = "用户名或者密码不能为空";
    private static final String USERNAME = "username";
    private static final String PASSWORD = "password";
    private final AuthenticationTokenGenerator authenticationTokenGenerator;

    public JwtAuthenticationFilter(String loginUrl, AuthenticationManager authenticationManager, AuthenticationTokenGenerator authenticationTokenGenerator) {
        super(new AntPathRequestMatcher(loginUrl, POST));
        this.setAuthenticationManager(authenticationManager);
        this.authenticationTokenGenerator = authenticationTokenGenerator;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        if (!request.getMethod().equals(POST)) {
            throw new AuthenticationServiceException(AUTHENTICATION_METHOD_NOT_SUPPORTS);
        }

        String username = null;
        String password = null;

        String contentType = request.getContentType();
        if (!StringUtils.isEmpty(contentType) && contentType.equalsIgnoreCase(MimeTypeUtils.APPLICATION_JSON_VALUE)) {
            try {
                JSONObject userJson = RequestUtils.paseRequestToJsonObject(request);
                username = userJson.get(USERNAME).toString();
                password = userJson.get(PASSWORD).toString();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        } else {
            username = request.getParameter(USERNAME);
            password = request.getParameter(PASSWORD);
        }

        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            sendError(response);
            return null;
        }
        AbstractAuthenticationToken token = authenticationTokenGenerator.generate(username.trim(), password);
        token.setDetails(authenticationDetailsSource.buildDetails(request));
        return this.getAuthenticationManager().authenticate(token);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        super.successfulAuthentication(request, response, chain, authResult);
    }

    private void sendError(HttpServletResponse response) {
        response.setContentType(MimeTypeUtils.APPLICATION_JSON_CHARSET_UTF8);
        try (PrintWriter writer = response.getWriter()) {
            ResultVO resultVO = new ResultVO(USERNAME_OR_PASSWORD_CAN_NOT_BE_NULL, false, null);
            writer.write(JsonUtils.toJSONString(resultVO));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
